package com.example.demo.config.two;

import org.springframework.beans.BeanUtils;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.util.StringUtils;
import org.springframework.validation.DataBinder;
import org.springframework.web.bind.ServletRequestDataBinder;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.servlet.HandlerMapping;

import javax.servlet.ServletRequest;
import java.util.*;

/**
 * @author <a href="juntao.zhang@bkjk.com">juntao.zhang</a>
 * @Description:
 * @Package com.bkjk.platform.uc.web.config
 * @date 2018/4/12 13:56
 * @see
 */
public class MyHandlerMethodArgumentResolver extends MyModelAttributeMethodProcessor {


    public MyHandlerMethodArgumentResolver(boolean annotationNotRequired) {
        super(annotationNotRequired);
    }

    @Override
    protected final Object createAttribute(String attributeName, MethodParameter methodParam, WebDataBinderFactory binderFactory, NativeWebRequest request) throws Exception {
        String value = this.getRequestValueForAttribute(attributeName, request);
        if (value != null) {
            Object attribute = this.createAttributeFromRequestValue(value, attributeName, methodParam, binderFactory, request);
            if (attribute != null) {
                return attribute;
            }
        }

        return createAttribute(methodParam);
    }


    protected Object createAttribute(MethodParameter parameter) throws Exception {
        if (Objects.equals(parameter.getParameterType(), List.class))
            return new LinkedList<>();
        if (Objects.equals(parameter.getParameterType(), Set.class))
            return new HashSet<>();
        if (Objects.equals(parameter.getParameterType(), Map.class))
            return new HashMap<>();
        return BeanUtils.instantiateClass(parameter.getParameterType());
    }


    protected String getRequestValueForAttribute(String attributeName, NativeWebRequest request) {
        Map<String, String> variables = this.getUriTemplateVariables(request);
        String variableValue = (String)variables.get(attributeName);
        if (StringUtils.hasText(variableValue)) {
            return variableValue;
        } else {
            String parameterValue = request.getParameter(attributeName);
            return StringUtils.hasText(parameterValue) ? parameterValue : null;
        }
    }

    protected final Map<String, String> getUriTemplateVariables(NativeWebRequest request) {
        Map<String, String> variables = (Map)request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, 0);
        return variables != null ? variables : Collections.emptyMap();
    }


    protected Object createAttributeFromRequestValue(String sourceValue, String attributeName, MethodParameter parameter, WebDataBinderFactory binderFactory, NativeWebRequest request) throws Exception {
        DataBinder binder = binderFactory.createBinder(request, (Object)null, attributeName);
        ConversionService conversionService = binder.getConversionService();
        if (conversionService != null) {
            TypeDescriptor source = TypeDescriptor.valueOf(String.class);
            TypeDescriptor target = new TypeDescriptor(parameter);
            if (conversionService.canConvert(source, target)) {
                return binder.convertIfNecessary(sourceValue, parameter.getParameterType(), parameter);
            }
        }

        return null;
    }


    @Override
    protected void bindRequestParameters(WebDataBinder binder, NativeWebRequest request) {
        ServletRequest servletRequest = (ServletRequest)request.getNativeRequest(ServletRequest.class);
        ServletRequestDataBinder servletBinder = (ServletRequestDataBinder)binder;
        servletBinder.bind(servletRequest);
    }
}

